﻿using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace CWM.Skinn
{
    public static partial class SkinnContext
    {
        [InitializeOnLoad]
        private class InitializeContext
        {
            static InitializeContext()
            {
                OnEnable();
                EditorApplication.hierarchyWindowItemOnGUI -= DragAndDropMerge.HierarchyDrag;
                EditorApplication.hierarchyWindowItemOnGUI += DragAndDropMerge.HierarchyDrag;
                EditorApplication.hierarchyWindowItemOnGUI -= ContextGUI;
                EditorApplication.hierarchyWindowItemOnGUI += ContextGUI;
                EditorApplication.update -= EditorUpdate;
                EditorApplication.update += EditorUpdate;

#if UNITY_2018_3_OR_NEWER
                EditorApplication.projectChanged -= EditorUpdate;
                EditorApplication.projectChanged += EditorUpdate;
#endif

#if UNITY_2019_1_OR_NEWER
                SceneView.duringSceneGui -= DuringSceneGui;
                SceneView.duringSceneGui += DuringSceneGui;
#else
                SceneView.onSceneGUIDelegate -= DuringSceneGui;
                SceneView.onSceneGUIDelegate += DuringSceneGui;
#endif
                var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
                if (!symbols.Contains("SKINN"))
                {
                    var defines = symbols.Split(';');
                    var newSymbols = "SKINN;";
                    for (int i = 0; i < defines.Length; i++)
                    {
                        newSymbols += defines[i].Replace(";","") + ";";
                    }
                    PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, newSymbols);
                }

                if (SkinnInternalAsset.Asset)
                {
                    if (PlayerSettings.gpuSkinning && !SkinnInternalAsset.Asset.contextSettings.allowUnityGPUSkinnig)
                    {
                        PlayerSettings.gpuSkinning = false;
                        Debug.LogWarning("Skinn: GPU Skinning turned off to avoid errors.");
                    }
                }
            }

            private static bool StartedComplie = false;

            private static void EditorUpdate()
            {
                var isCompiling = EditorApplication.isCompiling || (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode);

                if (isCompiling)
                {
                    if (!StartedComplie)
                    {
                        if (SkinnGizmos.OnEditorRelease != null) SkinnGizmos.OnEditorRelease();
                        StartedComplie = true;
                        SkinnGizmos.Release();
                        SkinnInternalAsset.ClearTempData();
                    }
                }
                else StartedComplie = false;
            }

            private static void DuringSceneGui(SceneView sceneView)
            {
                if (SceneView.currentDrawingSceneView != SceneView.lastActiveSceneView) return;
                if (SkinnGizmos.OnSceneGUI != null) SkinnGizmos.OnSceneGUI();
            }

            public static void ContextGUI(int id, Rect selectionRect)
            {
                MenuMain.ContextGUI(id, selectionRect);
                MenuBones.ContextGUI(id, selectionRect);
                MenuShapes.ContextGUI(id, selectionRect);
            }

            private static void OnEnable()
            {
                if (!SkinnInternalAsset.Asset || !SkinnInternalAsset.Asset.HasShaders())
                { }
#if CWM_DEV
                var methods = typeof(SkinnContextCommands).GetMethods();
                string commandsLog = typeof(SkinnContextCommands).Name;
                commandsLog += System.Environment.NewLine; commandsLog += System.Environment.NewLine;

                foreach (var context in methods)
                {
                    var attributes = context.GetCustomAttributes(true);
                    for (int i = 0; i < attributes.Length; i++)
                    {
                        var ctxItem = attributes[i] as CtxMenuItem; if (!ctxItem) continue;
                        commandsLog += string.Format("{0} : {1}", context.Name, ctxItem.menuItem);
                        commandsLog += System.Environment.NewLine;
                    }
                }
                Debug.Log(commandsLog);
#endif
            }
        }

        public static class ContextID
        {
            public const string SkinnedMeshRenderer = "SkinnedMeshRenderer";
            public const string MeshRenderer = "MeshRenderer";
            public const string Renderer = "Renderer";
            public const string Transform = "Transform";
            public const string GameObject = "GameObject";
            public const string Animator = "Animator";

            public static string Label(string value)
            {
                value = value.Replace(SkinnedMeshRenderer + "/", "");
                value = value.Replace(MeshRenderer + "/", "");
                value = value.Replace(Renderer + "/", "");
                value = value.Replace(Transform + "/", "");
                value = value.Replace(GameObject + "/", "");
                value = value.Replace(Animator + "/", "");
                return value;
            }

            internal const string BoneSubKey = "*Bone*";
            internal const string BlendshapeSubKey = "*Blend-shape*";
            //internal const string SubMeshSubKey = "*Sub-Mesh*";

        }
        
        public static class CopyRef
        {
            public static UnityEngine.Object Asset { get; internal set; }

            public static Mesh SharedMesh()
            {
                return SkinnEx.GetSharedMesh(Asset as Renderer);
            }
        }

        public static string ContextType = "";

        private static List<int> InstanceIDs = new List<int>();

        public static int ContextItemCount { get { return InstanceIDs.Count; } }

        public static int ContextBone0 = -1;
        public static int ContextBone1 = -1;

        public static string ContextShape0 = "";
        public static string ContextShape1 = "";

        public static List<GameObject> GetContextItems()
        {
            var gameObjects = new List<GameObject>();
            foreach (var item in InstanceIDs) { var go = SkinnEx.FindGameObject(item); if (go) gameObjects.Add(go); }
            foreach (var item in Selection.gameObjects) { if (!gameObjects.Contains(item)) gameObjects.Add(item); }
            InstanceIDs = new List<int>();

            return gameObjects;
        }

        public static GameObject GetContextItem()
        {
            GameObject gameObject = null;
            foreach (var item in InstanceIDs) { var go = SkinnEx.FindGameObject(item); if (go) { gameObject = go; } break; }
            InstanceIDs = new List<int>();

            if (gameObject == null)
            {
                gameObject = Selection.activeGameObject; //从Context之外指定物体
            }

            if (!SkinnEx.IsNullOrNotInAScene(gameObject)) gameObject.SetActive(true);
            return gameObject;
        }

        public static void LogWarning(string info = "", bool beep = true)
        {
            if (beep) EditorApplication.Beep();
            if (!string.IsNullOrEmpty(info)) Debug.LogWarning(info);
        }

        public static void Finished(string message = "", bool warning = false, bool warningSound = true)
        {
            if (warning) LogWarning(message, warningSound);
            if (Selection.activeGameObject) EditorGUIUtility.PingObject(Selection.activeGameObject);
            InstanceIDs = new List<int>();
            if (!Application.isPlaying) SkinnEditorEx.RepaintSceneView();
        }

        public static bool IsVertexCountOkay(Renderer renderer, string label = "Above Recommended Vertex Count!", int vertexCount = 10000)
        {
            var sharedMesh = renderer.GetSharedMesh();
            if (!sharedMesh) return false;
            if (sharedMesh.vertices.Length > vertexCount)
            {
                LogWarning();
                if (EditorUtility.DisplayDialog(label, renderer.GetSharedMesh().MeshDisplayInfo(), "Skip", "Continue")) return false;
            }
            return true;
        }

        public static void UndoRecord(Renderer renderer, string label)
        {
            var mf = renderer.GetComponent<MeshFilter>();
            if (mf) Undo.RecordObject(mf, label);
            Undo.RecordObject(renderer, label);
            Undo.RecordObject(renderer.gameObject, label);
        }

        public static void InvokeContext(string methodID)
        {
            var methods = typeof(SkinnContextCommands).GetMethods();
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(true);
                for (int i = 0; i < attributes.Length; i++)
                {
                    var ctxItem = attributes[i] as CtxMenuItem; if (!ctxItem) continue;
                    if (methodID == ctxItem.menuItem)
                    {
#if CWM_DEV
                        Debug.LogFormat("{0} : {1}", method, ctxItem.menuItem);
#endif
                        method.Invoke(null, null); return;
                    }
                }
            }
            GetContextItems();
        }

        public static void OptimizeMesh(SkinnedMeshRenderer smr, bool useSettings = true)
        {
            if (useSettings)
            {
                if (!SkinnInternalAsset.Asset || !SkinnInternalAsset.Asset.contextSettings.autoOptimzeBones || smr.sharedMesh.vertices.Length > 6500) return;
            }
            
            SkinnedMeshRenderer newAsset = null;
            smr.CloneOptimzeBones(out newAsset);
            if (newAsset)
            {
                smr.sharedMesh = newAsset.sharedMesh;
                smr.rootBone = newAsset.rootBone;
                smr.bones = newAsset.bones;
                smr.SetBounds();

                SkinnEx.Release(newAsset.gameObject);
            }
        }
    }
}
